package com.share.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.share.dao.FileDao;
import com.share.model.File;
import com.share.model.User;
import com.share.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FsStatus;
import org.apache.hadoop.fs.Path;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@Service
public class FileServiceImpl extends ServiceImpl<FileDao, File> implements FileService {

    @Autowired
    public FileSystem fileSystem;
    private static final Map<String, AtomicBoolean> INIT_FLAGS = new ConcurrentHashMap<>();     // 确保线程安全

    @Override
    public void uploadFile(User user, File file) {
        String phone = user.getPhone();
        AtomicBoolean initFlag = INIT_FLAGS.computeIfAbsent(phone, k -> new AtomicBoolean(true));       // 原子地获取并设置标志位的状态 避免竞态条件
        try {
            // 1. 初始用户目录
            String save_path = "/" + phone + "/";
            if (initFlag.getAndSet(false)) {         // 只有第一次为 true 时执行初始化
                fileSystem.mkdirs(new Path(save_path));        // 创建用户根目录
                fileSystem.mkdirs(new Path(save_path + "/bin"));        // 创建用户回收站目录
                file.setFileURL("/" + phone + "/");
                file.setDirectory(true);
                file.setCreateTime(new Date());
                file.setUpdateTime(new Date());
                save(file);
                file.setFileURL("/" + phone + "/bin");
                file.setDirectory(true);
                file.setCreateTime(new Date());
                file.setUpdateTime(new Date());
                save(file);
                log.info("初始化 HDFS 目录 OK");
            }

            // 2. 上传文件到 HDFS
            String fileName = file.getFileURL().substring(file.getFileURL().lastIndexOf("\\") + 1);
            log.info("fileName:{}", fileName);
            fileSystem.copyFromLocalFile(new Path(file.getFileURL()), new Path(save_path));

            // 3. 将文件信息写到数据库
            FileStatus status = fileSystem.getFileStatus(new Path(save_path + fileName));
            file.setFileName(fileName);
            if (status.isDirectory()) {
                file.setDirectory(true);        // 1
            }
            file.setDirectory(false);       // 0
            String fileURL = status.getPath().toString();
            file.setFileURL(fileURL);
            file.setParentURL(fileURL.substring(0, fileURL.lastIndexOf("/")));
            long capacity = status.getLen();
            log.info("capacity:{}", capacity);
            file.setFileSize((capacity / 1024) + "KB");
            file.setUser_id(user.getId());
            file.setCreateTime(new Date());
            file.setUpdateTime(new Date());
            save(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void updateMySQL(User user, File file) {
        updateById(file);
    }

    @Override
    public void delete(String fileName) {
        try {
            fileSystem.delete(new Path(""), true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void select(User user) {
        try {
          listPath(new Path("/" + user.getPhone() + "/"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void listPath(Path path) throws IOException {
        FileStatus fileStatus = fileSystem.getFileStatus(path);
        if (fileStatus.isDirectory()) {         // 是目录的话执行递归
            System.out.println("这是文件夹" + fileStatus.getPath());
            FileStatus[] fileStatuses = fileSystem.listStatus(fileStatus.getPath());
            for (FileStatus status : fileStatuses) {        // 再次递归文件夹下的文件和文件夹去进行判断
                listPath(status.getPath());     // 递归获取该目录下的文件
            }
        } else {
            System.out.println("这是文件" + fileStatus.getPath());
        }
    }

}
